package is.glif.fenius.translators.harmony;

import is.glif.fenius.api.FCancelReq;
import is.glif.fenius.api.FCancelResp;
import is.glif.fenius.api.FCreateReq;
import is.glif.fenius.api.FCreateResp;
import is.glif.fenius.api.FExternalFault;
import is.glif.fenius.api.FInternalFault;
import is.glif.fenius.api.FListReq;
import is.glif.fenius.api.FListResp;
import is.glif.fenius.api.FQueryReq;
import is.glif.fenius.api.FQueryResp;
import is.glif.fenius.api.FReservationParams;
import is.glif.fenius.api.Translator;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Hashtable;
import java.util.Set;
import java.util.Vector;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.muse.ws.addressing.soap.SoapFault;

import eu.ist_phosphorus.harmony.common.serviceinterface.databinding.jaxb.CancelReservationResponseType;
import eu.ist_phosphorus.harmony.common.serviceinterface.databinding.jaxb.CreateReservationResponseType;
import eu.ist_phosphorus.harmony.common.serviceinterface.databinding.jaxb.GetReservationsComplexType;
import eu.ist_phosphorus.harmony.common.serviceinterface.databinding.jaxb.GetReservationsResponseType;
import eu.ist_phosphorus.harmony.common.serviceinterface.databinding.jaxb.GetReservationsType;
import eu.ist_phosphorus.harmony.common.serviceinterface.databinding.jaxb.GetStatusResponseType;
import eu.ist_phosphorus.harmony.common.serviceinterface.databinding.jaxb.StatusType;
import eu.ist_phosphorus.harmony.common.serviceinterface.reservation.SimpleReservationClient;

public class HarmonyTranslator implements Translator {

	/** The reservation client. */
	private final SimpleReservationClient client;

	/** Logger **/
	private Log logger = LogFactory.getLog(HarmonyTranslator.class);

	/** Reservation ID mapper **/
	private Hashtable<String, String> reservation = new Hashtable<String, String>(
			10, 10);

	// TODO Add it in config file. Not loaded in memory. Only for demo purposes
	/** Endpoint mapper **/
	private Hashtable<String, String> endpoints = new Hashtable<String, String>(
			10, 10);

	// TODO Add it in config file. Only for demo purposes
	/** Auxiliar Fenius endpoints **/
	private Vector<String> feniusEndpoints = new Vector<String>(3);

	public final boolean debug = true;

	// Vlan tag. Only for monitoring system.
	private String vlan = null;

	public HarmonyTranslator() {
		// Initialize the client with the EPR of the IDB to call
		// TODO Read the EPR from a properties file
		client = new SimpleReservationClient(
				"http://84.88.32.43:8080/harmony-idb/services/Reservation");

		// TODO Initialize the endpoints in a more elegant way (From a
		// properties file)
		endpoints.put("net.hpdm:synchromedia:ue/videoCam", "10.3.3.1");
		endpoints.put("net.hpdm:i2cat:ue/displayCam", "10.3.3.8");
		endpoints.put("net.hpdm:synchromedia:ue/clusterSite", "10.3.3.9");
		endpoints.put("net.hpdm.synchromedia:be/2", "10.3.3.2");

		// TODO Remove this weird...
		feniusEndpoints.add("net.hpdm:synchromedia:ue/videoCam");
		feniusEndpoints.add("net.hpdm:i2cat:ue/displayCam");
		feniusEndpoints.add("net.hpdm:synchromedia:ue/clusterSite");
		feniusEndpoints.add("net.hpdm:synchromedia:be/2");

	}

	public FCreateResp create(FCreateReq r) throws FInternalFault,
			FExternalFault {

		System.err.println("Harmony Create start");
		logger.info("Harmony Create Reservation process started");

		FCreateResp resp = null;
		resp = new FCreateResp();
		FReservationParams reqParams = r.getResvParams();

		// Start translation Fenius to Harmony

		// Endpoints
		String sourceToTranslate = reqParams.getSrcEndpoint();
		String targetToTranslate = reqParams.getDstEndpoint();

		String source = this.endpoints.get(sourceToTranslate);
		logger.info("SOURCE: " + source);
		String target = this.endpoints.get(targetToTranslate);
		logger.info("TARGET: " + target);

		// Delay hardcoded
		int delay = 500;
		int bandwidth = reqParams.getBandwidth();
		int duration = reqParams.getDuration();

		// FIXME: not needed??
		this.vlan = reqParams.getSrcVlanTag();

		GregorianCalendar aux = new GregorianCalendar();
		aux.setTime(reqParams.getActivationTime());
		XMLGregorianCalendar startTime = null;

		try {
			startTime = DatatypeFactory.newInstance().newXMLGregorianCalendar(
					aux);
		} catch (DatatypeConfigurationException e) {
			throw new RuntimeException(
					"A serious configuration error was detected ...", e);
		}

		CreateReservationResponseType createHarmonyResult = null;

		logger.info("Sending create reservation request to Harmony");
		if (debug) {
			logger.info("Using:");
			logger.info("Source: " + source);
			logger.info("Target: " + target);
			logger.info("Delay: " + delay);
			logger.info("Bandwidth: " + bandwidth);
			logger.info("Duration: " + duration);
		}

		// create reservation request to Harmony IDB
		try {
			createHarmonyResult = client.createReservation(source, target,
					startTime, bandwidth, delay, duration);
		} catch (org.apache.muse.ws.addressing.soap.SoapFault e) {
			throw new RuntimeException(
					"A serious error detected in Harmony while creating the reservation: "
							+ e.getMessage(), e);
		}
		if (reqParams.getReservationId() != null) {

			// Keep relation Fenius ID - Harmony ID in order to cancel/list
			// reservations
			reservation.put(reqParams.getReservationId(), createHarmonyResult
					.getReservationID());
		} else {
			reservation.put(createHarmonyResult.getReservationID(),
					createHarmonyResult.getReservationID());
		}

		if (debug) {
			System.err.println("FENIUS Reservation ID: "
					+ reqParams.getReservationId());
			System.err.println("HARMONY Reservation ID: "
					+ createHarmonyResult.getReservationID());
			logger.info("FENIUS Reservation ID: "
					+ reqParams.getReservationId());
			logger.info("HARMONY Reservation ID: "
					+ createHarmonyResult.getReservationID());
		}

		// Parse reservation result
		FReservationParams respParams = new FReservationParams();
		respParams.setReservationId(reqParams.getReservationId());
		respParams.setActivationTime(reqParams.getActivationTime());
		respParams.setDuration(reqParams.getDuration());
		respParams.setSrcEndpoint(reqParams.getSrcEndpoint());
		respParams.setDstEndpoint(reqParams.getDstEndpoint());
		respParams.setStatus("RESERVED");
		respParams.setBandwidth(reqParams.getBandwidth());
		resp.setResvParams(respParams);

		System.err.println("Harmony create reservation finished");
		logger.info("Harmony create reservation finished");
		// Return
		return resp;

	}

	public FListResp list(FListReq r) throws FInternalFault, FExternalFault {
		FListResp resp = new FListResp();

		System.err.println("Harmony list reservations started");

		ArrayList<FReservationParams> reservations = new ArrayList<FReservationParams>();

		// Get reservations from Harmony IDB
		GetReservationsResponseType getResvResult = null;
		GetReservationsType getResvRequest = new GetReservationsType();

		// Start and end time of the interval for listing the reservations
		GregorianCalendar start = new GregorianCalendar();
		start.add(Calendar.HOUR, 4);
		XMLGregorianCalendar startTime = null;

		try {
			startTime = DatatypeFactory.newInstance().newXMLGregorianCalendar(
					start);
		} catch (DatatypeConfigurationException e) {
			throw new RuntimeException(
					"A serious configuration error was detected ...", e);
		}
		getResvRequest.setPeriodEndTime(startTime);

		GregorianCalendar end = new GregorianCalendar();
		end.set(Calendar.HOUR, end.get(Calendar.HOUR) - 1);
		XMLGregorianCalendar endTime = null;

		try {
			endTime = DatatypeFactory.newInstance()
					.newXMLGregorianCalendar(end);
		} catch (DatatypeConfigurationException e) {
			throw new RuntimeException(
					"A serious configuration error was detected: "
							+ e.getMessage(), e);
		}

		getResvRequest.setPeriodStartTime(endTime);

		// Call Harmony service
		try {
			getResvResult = this.client.getReservations(getResvRequest);
		} catch (org.apache.muse.ws.addressing.soap.SoapFault e) {
			throw new RuntimeException(
					"A serious error detected in Harmony while listing the reservations",
					e);
		}

		// Loop over the reservations in the response
		for (GetReservationsComplexType getReservation : getResvResult
				.getReservation()) {

			FReservationParams FGetReservation = new FReservationParams();
			// Translate resv ID
			if (this.obtainResvId(this.reservation.keySet(), getReservation
					.getReservationID()) == null) {
				FGetReservation.setReservationId(getReservation
						.getReservationID());
			} else {
				FGetReservation.setReservationId(this.obtainResvId(
						this.reservation.keySet(), getReservation
								.getReservationID()));
			}

			System.out.println(getReservation.getReservationID());
			// Src and Dst endpoint
			String fenius_srcEP = this.obtainEndpointId(this.feniusEndpoints,
					getReservation.getReservation().getService().get(0)
							.getConnections().get(0).getSource()
							.getEndpointId());
			FGetReservation.setSrcEndpoint(fenius_srcEP);

			String fenius_dstEP = this.obtainEndpointId(this.feniusEndpoints,
					getReservation.getReservation().getService().get(0)
							.getConnections().get(0).getTarget().get(0)
							.getEndpointId());
			FGetReservation.setDstEndpoint(fenius_dstEP);

			// bw
			FGetReservation.setBandwidth(getReservation.getReservation()
					.getService().get(0).getConnections().get(0).getMinBW());
			// duration
			FGetReservation.setDuration(getReservation.getReservation()
					.getService().get(0).getFixedReservationConstraints()
					.getDuration());
			// activation time
			Date activationTime = getReservation.getReservation().getService()
					.get(0).getFixedReservationConstraints().getStartTime()
					.toGregorianCalendar().getTime();
			FGetReservation.setActivationTime(activationTime);
			// vlan tag
			FGetReservation.setSrcVlanTag(this.vlan);
			FGetReservation.setDstVlanTag(this.vlan);

			// Get the status of the reservation
			GetStatusResponseType status = null;
			try {
				status = this.client.getStatus(getReservation
						.getReservationID());
			} catch (SoapFault e) {
				throw new RuntimeException(
						"A serious error happened in Harmony IDB while getting the status");
			}

			if (null != status.getServiceStatus().get(0).getStatus()) {

				StatusType toCmp = status.getServiceStatus().get(0).getStatus();
				if (toCmp.equals(StatusType.ACTIVE)) {
					FGetReservation.setStatus("ACTIVE");
				} else if (toCmp.equals(StatusType.CANCELLED_BY_SYSTEM)) {
					FGetReservation.setStatus("CANCELLED BY SYSTEM");
				} else if (toCmp.equals(StatusType.CANCELLED_BY_USER)) {
					FGetReservation.setStatus("CANCELLED_BY_USER");
				} else if (toCmp.equals(StatusType.COMPLETED)) {
					FGetReservation.setStatus("FINISHED");
				} else if (toCmp.equals(StatusType.PENDING)) {
					FGetReservation.setStatus("RESERVED");
				} else if (toCmp.equals(StatusType.TEARDOWN_IN_PROGRESS)) {
					FGetReservation.setStatus("TEAR DOWN IN PROGRESS");
				} else if (toCmp.equals(StatusType.SETUP_IN_PROGRESS)) {
					FGetReservation.setStatus("SETUP IN PROGRESS");
				} else {
					FGetReservation.setStatus("UNKNOWN");
				}
			}

			reservations.add(FGetReservation);

		}

		resp.setReservations(reservations);
		return resp;
	}

	public FCancelResp cancel(FCancelReq r) throws FInternalFault,
			FExternalFault {

		System.err.println("Harmony cancel reservation started");

		FCancelResp resp = new FCancelResp();

		// Translate request Fenius to Harmony
		String resvId = r.getResvParams().getReservationId();

		// TODO Not definitive resv mapping
		String harmonyReservationId = reservation.get(resvId);

		if (harmonyReservationId == null) {
			throw new RuntimeException("No reservation mapping valid");
		}

		CancelReservationResponseType cancelResult = null;

		// cancel reservation request to Harmony IDB
		try {
			cancelResult = client.cancelReservation(harmonyReservationId);
		} catch (org.apache.muse.ws.addressing.soap.SoapFault e) {
			throw new RuntimeException(
					"A serious error detected in Harmony while cancelling the reservation",
					e);
		}

		if (cancelResult.isSuccess()) {
			resp.setResvParams(r.getResvParams());
			resp.getResvParams().setStatus("CANCELLED");
			System.err.println("Harmony cancel reservation finished");
			return resp;
		} else {
			return null;
		}
	}

	/**
	 * Given the keys set of the Hashtable and the reservation ID from Harmony
	 * returns the corresponding reservation ID from FENIUS
	 *
	 * @param set
	 * @return
	 */
	private String obtainResvId(Set<String> set, String harmonyResvId) {

		for (String FeniusId : set) {
			if (reservation.get(FeniusId).equals(harmonyResvId)) {
				return FeniusId;
			}
		}
		return null;
	}

	/**
	 * Given the keys set of the Hashtable and the endpoint ID from Harmony
	 * returns the corresponding endpoint ID from FENIUS
	 *
	 * @param set
	 * @return
	 */
	private String obtainEndpointId(Vector<String> set, String harmonyEP) {

		for (String feniusEP : set) {
			if (endpoints.get(feniusEP).equals(harmonyEP)) {
				return feniusEP;
			}
		}
		return null;
	}

	public FQueryResp query(FQueryReq r) throws FInternalFault, FExternalFault {
		// TODO Auto-generated method stub
		return null;
	}
}
